/* mexHID.c
 * mexHID
 *
 * mexHID defines C "mex" routines for working with USB "Human Interface Devices" from Matlab.
 * mexHID can detect and configure HID devices and exchange data with them.
 *
 * mexHID.c defines the Matlab "mex" interface, only, and implements no functionality.
 *
 * The HID functionality required for mexHID is specified in some detail in mexHID.h.
 * The realization of that functionality must be platform-specific.  Any realization of mexHID 
 * should implement all the functions from mexHID.h, and incorporate mexHID.c.
 *
 * By Benjamin Heasly, University of Pennsylvania, 22 Feb. 2010
 */

#include "mexHID.h"
#include <string.h>

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) {
    int status = 0;
    char *command = NULL;
	
	mexAtExit(&mexHIDExit);
	
	// First argument should be a command string
    if(nrhs >= 1 && mxIsChar(prhs[0]) && mxGetM(prhs[0])==1)
        command = mxArrayToString(prhs[0]);
    
    if (command != NULL) {
		
        if(!strcmp(command, "initialize")) {
            //mexLock();
            status = mexHIDInitialize();
            
        } else if (!strcmp(command, "terminate")) {
            //while (mexIsLocked())
            //    mexUnlock();
            status = mexHIDTerminate();
			
		} else if (!strcmp(command, "isInitialized")) {
            status = mexHIDIsInitialized();
			
		} else if (!strcmp(command, "getOpenedDevices")) {
            mxArray* opened = mexHIDGetAllOpenDevices();
			if (opened != NULL)
				plhs[0] = opened;
			else
				plhs[0] = mxCreateDoubleMatrix(0, 0, mxREAL);
			mxFree(command);
			return;
			
		} else if (!strcmp(command, "step")) {
            mexHIDStep();
            
        } else if (!strcmp(command, "summarizeDevices")) {
            mxArray* summary = mexHIDGetPropertiesForAllDevices();
			if (summary != NULL) {
				plhs[0] = summary;
				mxFree(command);
				return;
			} else
				status = -30;
			
		} else if (!strcmp(command, "openMatchingDevice")) {
			if(nrhs >= 2 && mxIsStruct(prhs[1])) {
				mexHIDDeviceID deviceID = mexHIDOpenDeviceMatchingProperties(prhs[1]);
				if (deviceID >= 0) {
					plhs[0] = mxCreateDoubleScalar((double)deviceID);
					mxFree(command);
					return;
				} else
					status = -45;
			} else
				status = -40;
			
		} else if (!strcmp(command, "closeDevice")) {
			if(nrhs >= 2 && mxIsDouble(prhs[1])) {
				mexHIDDeviceID deviceID = (mexHIDDeviceID)mxGetScalar(prhs[1]);
				status = mexHIDCloseDevice(deviceID);
			} else
				status = -50;
			
		} else if (!strcmp(command, "getDeviceProperties")) {
			if(nrhs >= 2 && mxIsDouble(prhs[1])) {
				mexHIDDeviceID deviceID = (mexHIDDeviceID)mxGetScalar(prhs[1]);
				mxArray* props = mexHIDGetPropertiesForDevice(deviceID);
				if (props != NULL) {
					plhs[0] = props;
					mxFree(command);
					return;
				} else
					status = -65;
			} else
				status = -60;
			
		} else if (!strcmp(command, "summarizeElements")) {
			if(nrhs >= 2 && mxIsDouble(prhs[1])) {
				mexHIDDeviceID deviceID = (mexHIDDeviceID)mxGetScalar(prhs[1]);
				mxArray* elements = NULL;
				if(nrhs >= 3 && mxIsDouble(prhs[2]))
					elements = mexHIDGetAllPropertiesForDeviceElements(deviceID, prhs[2]);
				else
					elements = mexHIDGetAllPropertiesForAllDeviceElements(deviceID);
				
				if (elements != NULL) {
					plhs[0] = elements;
					mxFree(command);
					return;
				} else
					status = -75;
			} else
				status = -70;
			
		} else if (!strcmp(command, "findMatchingElements")) {
			if(nrhs >= 3 && mxIsDouble(prhs[1]) && mxIsStruct(prhs[2])) {
				mexHIDDeviceID deviceID = (mexHIDDeviceID)mxGetScalar(prhs[1]);
				mxArray* elementCookies = mexHIDFindDeviceElementsMatchingProperties(deviceID, prhs[2]);
				if (elementCookies != NULL) {
					plhs[0] = elementCookies;
					mxFree(command);
					return;
				} else
					status = -85;
			} else
				status = -80;
			
		} else if (!strcmp(command, "getElementProperties")) {
			if(nrhs >= 4 && mxIsDouble(prhs[1]) && mxIsDouble(prhs[2]) && mxIsCell(prhs[3])) {
				mexHIDDeviceID deviceID = (mexHIDDeviceID)mxGetScalar(prhs[1]);
				mxArray* values = mexHIDGetPropertiesForDeviceElements(deviceID, prhs[2], prhs[3]);
				if (values != NULL) {
					plhs[0] = values;
					mxFree(command);
					return;
				} else
					status = -95;
			} else
				status = -90;
			
		} else if (!strcmp(command, "setElementProperties")) {
			if(nrhs >= 4 && mxIsDouble(prhs[1]) && mxIsDouble(prhs[2]) && mxIsStruct(prhs[3])) {
				mexHIDDeviceID deviceID = (mexHIDDeviceID)mxGetScalar(prhs[1]);
				status = mexHIDSetPropertiesForDeviceElements(deviceID, prhs[2], prhs[3]);
			} else
				status = -100;
			
		} else if (!strcmp(command, "readElementValues")) {
			if(nrhs >= 3 && mxIsDouble(prhs[1]) && mxIsDouble(prhs[2])) {
				mexHIDDeviceID deviceID = (mexHIDDeviceID)mxGetScalar(prhs[1]);
				mxArray* values = mexHIDReadDeviceElementValues(deviceID, prhs[2]);
				if (values != NULL) {
					plhs[0] = values;
					mxFree(command);
					return;
				} else
					status = -115;
			} else
				status = -110;
			
		} else if (!strcmp(command, "writeElementValues")) {
			if(nrhs >= 3 && mxIsDouble(prhs[1]) && mxIsDouble(prhs[2]) && mxIsDouble(prhs[3])) {
				mexHIDDeviceID deviceID = (mexHIDDeviceID)mxGetScalar(prhs[1]);
				status = mexHIDWriteDeviceElementValues(deviceID, prhs[2], prhs[3]);
			} else
				status = -120;
			
		} else if (!strcmp(command, "openQueue")) {
			if(nrhs >= 5 && mxIsDouble(prhs[1]) && mxIsDouble(prhs[2]) && mxIsCell(prhs[3]) && mxIsDouble(prhs[4])) {
				mexHIDDeviceID deviceID = (mexHIDDeviceID)mxGetScalar(prhs[1]);
				int queueDepth = mxGetScalar(prhs[4]);
				if (mxGetM(prhs[3]) * mxGetN(prhs[3]) == 2 && mxIsFunctionHandle(mxGetCell(prhs[3], 0)))
					status = mexHIDOpenQueueForDeviceElementsWithMatlabCallbackAndDepth(deviceID, prhs[2], prhs[3], queueDepth);
				else 
					status = -135;
			} else
				status = -130;
			
		} else if (!strcmp(command, "closeQueue")) {
			if(nrhs >= 2 && mxIsDouble(prhs[1])) {
				mexHIDDeviceID deviceID = (mexHIDDeviceID)mxGetScalar(prhs[1]);
				status = mexHIDCloseQueue(deviceID);
			} else
				status = -140;
			
		} else if (!strcmp(command, "startQueue")) {
			if(nrhs >= 2 && mxIsDouble(prhs[1])) {
				mexHIDDeviceID deviceID = (mexHIDDeviceID)mxGetScalar(prhs[1]);
				status = mexHIDStartQueue(deviceID);
			} else
				status = -150;
			
		} else if (!strcmp(command, "stopQueue")) {
			if(nrhs >= 2 && mxIsDouble(prhs[1])) {
				mexHIDDeviceID deviceID = (mexHIDDeviceID)mxGetScalar(prhs[1]);
				status = mexHIDStopQueue(deviceID);
			} else
				status = -160;
			
		} else if (!strcmp(command, "flushQueue")) {
			if(nrhs >= 2 && mxIsDouble(prhs[1])) {
				mexHIDDeviceID deviceID = (mexHIDDeviceID)mxGetScalar(prhs[1]);
				status = mexHIDFlushQueue(deviceID);
			} else
				status = -170;
			
		} else
			status = -10;
		
		mxFree(command);
		plhs[0] = mxCreateDoubleScalar((double)status);
		
	} else {
		
		mexPrintf("mexHID basics:\n %s\n %s\n %s\n %s\n\n",
				  "status = mexHID('initialize')",
				  "status = mexHID('terminate')",
				  "status = mexHID('step')",
				  "isInitialized = mexHID('isInitialized')");
		
		mexPrintf("mexHID devices:\n %s\n %s\n %s\n %s\n %s\n\n",
				  "deviceIDs = mexHID('getOpenedDevices')",
				  "infoStruct = mexHID('summarizeDevices')",
				  "deviceID = mexHID('openMatchingDevice', matchingStruct)",
				  "status = mexHID('closeDevice', deviceID)",
				  "infoStruct = mexHID('getDeviceProperties', deviceID)");
		
		mexPrintf("mexHID device elements:\n %s\n %s\n %s\n %s\n %s\n %s\n\n",
				  "infoStruct = mexHID('summarizeElements', deviceID [,matchingStruct]);",
				  "cookies = mexHID('findMatchingElements', deviceID, matchingStruct)",
				  "infoStruct = mexHID('getElementProperties', deviceID, cookies, propertyNameCell)",
				  "status = mexHID('setElementProperties', deviceID, cookies, propertyValueStruct)",
				  "values = mexHID('readElementValues', deviceID, cookies)",
				  "status = mexHID('writeElementValues', deviceID, cookies, values)");
		
		mexPrintf("mexHID device queues:\n %s\n %s\n %s\n %s\n %s\n\n",
				  "status = mexHID('openQueue', deviceID, cookies, callbackCell, queueDepth)",
				  "status = mexHID('closeQueue', deviceID)",
				  "status = mexHID('startQueue', deviceID)",
				  "status = mexHID('stopQueue', deviceID)",
				  "status = mexHID('flushQueue', deviceID)");
	}
}